package net.mimai.vultrue.bus.mybatisplus.config;

import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import net.mimai.vultrue.bus.constants.CommonBizConstants;
import net.mimai.vultrue.bus.mybatisplus.expression.NullTenantValue;
import net.mimai.vultrue.bus.mybatisplus.interceptor.TenantBizInnerInterceptor;
import net.mimai.vultrue.bus.utils.FrameBoostUtil;
import net.mimai.vultrue.framework.utils.FrameworkUtils;
import net.mimai.vultrue.framework.utils.JudgeUtils;
import net.mimai.vultrue.framework.utils.ObjectMapperUtils;
import net.sf.jsqlparser.expression.LongValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import net.sf.jsqlparser.expression.Expression;

import java.util.List;

/**
 * @ClassName MybatisPlusTenantConfig
 * @Description 对于不要使用租户的sql，可在mapper上使用注解 @SqlParser(filter = true) ，但不推荐，参见 https://blog.csdn.net/weixin_38111957/article/details/101161660
 * session含租户信息的用户，查询包含字段tenant_code的表会自动走租户策略
 * @Author 方丈
 * @Date 2021/10/27 11:07
 * @Version 1.0.0
 **/
@Configuration
public class MybatisPlusConfig {
    private Logger log = LoggerFactory.getLogger(MybatisPlusConfig.class);
    /**
     * 新多租户插件配置,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存万一出现问题
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        TenantBizInnerInterceptor tenantLineInnerInterceptor = new TenantBizInnerInterceptor(new TenantLineHandler() {
            @Override
            public String getTenantIdColumn() {
                return CommonBizConstants.defaultTenantColumnName;
            }

            @Override
            public Expression getTenantId() {
                Object fromTokenExtJson = FrameBoostUtil.getFromTokenExtJson(CommonBizConstants.defaultTenantFieldName);
                if(JudgeUtils.isNull(fromTokenExtJson)){
                    return new NullTenantValue();
                }
                Long tenantCode =(Long) fromTokenExtJson;
                if(JudgeUtils.isNull(tenantCode)){
                    return new NullTenantValue();
                }
                //顶级机构，不受租户约束
                if(tenantCode == CommonBizConstants.topTenantCode){
                    return new NullTenantValue();
                }
                return new LongValue(tenantCode);
            }
            // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
            @Override
            public boolean ignoreTable(String tableName) {
                //框架表
                if(tableName.toLowerCase().startsWith("egl_")){
                    return true;
                }
                TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);
                if(tableInfo == null){
                    log.warn("重要告警: table {}'s tableInfo is null",tableName);
                    return false;
                }
                List<TableFieldInfo> fieldList = tableInfo.getFieldList();
                long count = fieldList.stream().filter(f -> JudgeUtils.equals(CommonBizConstants.defaultTenantColumnName, f.getColumn())).count();
                boolean ignore = count<1;
                if(ignore){
                    log.debug("[{}]不是多租户表",tableName);
                }
                return ignore;
            }
        });
        interceptor.addInnerInterceptor(tenantLineInnerInterceptor);

        // 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor
        // 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());

        //添加阻断器，防止全表操作
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        return interceptor;
    }
}
